import { Server as SocketIOServer, Socket } from 'socket.io';
import { AuthController } from '../controllers/AuthController';

const authController = new AuthController();

export function setupWebSocket(io: SocketIOServer) {
  // 认证中间件
  io.use(async (socket, next) => {
    try {
      const token = socket.handshake.auth.token;
      
      if (!token) {
        return next(new Error('No token provided'));
      }

      const user = await authController.verifyToken(token);
      if (!user) {
        return next(new Error('Invalid token'));
      }

      socket.data.user = user;
      next();
    } catch (error) {
      next(new Error('Authentication failed'));
    }
  });

  // 连接处理
  io.on('connection', (socket: Socket) => {
    const user = socket.data.user;
    console.log(`User ${user.email} connected via WebSocket`);

    // 加入用户房间
    socket.join(`user:${user.id}`);
    
    // 如果是管理员，加入管理员房间
    if (user.isAdmin()) {
      socket.join('admin');
    }

    // 发送连接成功消息
    socket.emit('connected', {
      message: 'Connected successfully',
      user: user.toSafeJSON(),
      timestamp: new Date().toISOString(),
    });

    // 处理任务订阅
    socket.on('subscribe:tasks', (data) => {
      const { taskIds } = data;
      
      if (Array.isArray(taskIds)) {
        taskIds.forEach(taskId => {
          socket.join(`task:${taskId}`);
        });
        
        socket.emit('subscribed:tasks', {
          taskIds,
          message: 'Subscribed to task updates',
        });
      }
    });

    // 处理任务取消订阅
    socket.on('unsubscribe:tasks', (data) => {
      const { taskIds } = data;
      
      if (Array.isArray(taskIds)) {
        taskIds.forEach(taskId => {
          socket.leave(`task:${taskId}`);
        });
        
        socket.emit('unsubscribed:tasks', {
          taskIds,
          message: 'Unsubscribed from task updates',
        });
      }
    });

    // 处理系统监控订阅（仅管理员）
    socket.on('subscribe:system', () => {
      if (user.isAdmin()) {
        socket.join('system');
        socket.emit('subscribed:system', {
          message: 'Subscribed to system updates',
        });
      } else {
        socket.emit('error', {
          message: 'Admin access required for system monitoring',
        });
      }
    });

    // 处理断开连接
    socket.on('disconnect', (reason) => {
      console.log(`User ${user.email} disconnected: ${reason}`);
    });

    // 错误处理
    socket.on('error', (error) => {
      console.error(`WebSocket error for user ${user.email}:`, error);
    });
  });

  return io;
}

// WebSocket 事件发送器
export class WebSocketEmitter {
  constructor(private io: SocketIOServer) {}

  // 发送任务更新
  emitTaskUpdate(taskId: string, data: any) {
    this.io.to(`task:${taskId}`).emit('task:update', {
      taskId,
      ...data,
      timestamp: new Date().toISOString(),
    });
  }

  // 发送任务完成
  emitTaskCompleted(taskId: string, result: any) {
    this.io.to(`task:${taskId}`).emit('task:completed', {
      taskId,
      result,
      timestamp: new Date().toISOString(),
    });
  }

  // 发送任务失败
  emitTaskFailed(taskId: string, error: string) {
    this.io.to(`task:${taskId}`).emit('task:failed', {
      taskId,
      error,
      timestamp: new Date().toISOString(),
    });
  }

  // 发送数据提取完成
  emitDataExtracted(taskId: string, data: any) {
    this.io.to(`task:${taskId}`).emit('data:extracted', {
      taskId,
      data,
      timestamp: new Date().toISOString(),
    });
  }

  // 发送系统监控数据（仅管理员）
  emitSystemMetrics(metrics: any) {
    this.io.to('system').emit('system:metrics', {
      ...metrics,
      timestamp: new Date().toISOString(),
    });
  }

  // 发送队列状态更新
  emitQueueStatus(queueName: string, status: any) {
    this.io.to('admin').emit('queue:status', {
      queueName,
      status,
      timestamp: new Date().toISOString(),
    });
  }

  // 发送用户通知
  emitUserNotification(userId: string, notification: any) {
    this.io.to(`user:${userId}`).emit('notification', {
      ...notification,
      timestamp: new Date().toISOString(),
    });
  }

  // 广播系统公告
  broadcastAnnouncement(announcement: any) {
    this.io.emit('announcement', {
      ...announcement,
      timestamp: new Date().toISOString(),
    });
  }
}
